/*-------------------------------------------------------------------------
 *
 * ifaddr.c
 *	  IP子网掩码计算以及网络接口的枚举。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/libpq/ifaddr.c
 *
 * 本文件及IPV6实现最初由
 * Nigel Kukard <nkukard@lbsd.net>, Linux 基于系统设计
 * http://www.lbsd.net.
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include <unistd.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#include <sys/file.h>

#include "libpq/ifaddr.h"
#include "port/pg_bswap.h"

static int	fc_range_sockaddr_AF_INET(const struct sockaddr_in *fc_addr,
								   const struct sockaddr_in *fc_netaddr,
								   const struct sockaddr_in *fc_netmask);

#ifdef HAVE_IPV6
static int	fc_range_sockaddr_AF_INET6(const struct sockaddr_in6 *fc_addr,
									const struct sockaddr_in6 *fc_netaddr,
									const struct sockaddr_in6 *fc_netmask);
#endif


/*
 * pg_range_sockaddr - addr 是否在由 netaddr/netmask 指定的子网内？
 *
 * 注意：调用者必须已经验证所有三个地址在同一地址族中；且不支持 AF_UNIX 地址。
 */
int pg_range_sockaddr(const struct sockaddr_storage *fc_addr,
				  const struct sockaddr_storage *fc_netaddr,
				  const struct sockaddr_storage *fc_netmask)
{
	if (fc_addr->ss_family == AF_INET)
		return fc_range_sockaddr_AF_INET((const struct sockaddr_in *) fc_addr,
									  (const struct sockaddr_in *) fc_netaddr,
									  (const struct sockaddr_in *) fc_netmask);
#ifdef HAVE_IPV6
	else if (fc_addr->ss_family == AF_INET6)
		return fc_range_sockaddr_AF_INET6((const struct sockaddr_in6 *) fc_addr,
									   (const struct sockaddr_in6 *) fc_netaddr,
									   (const struct sockaddr_in6 *) fc_netmask);
#endif
	else
		return 0;
}

static int fc_range_sockaddr_AF_INET(const struct sockaddr_in *fc_addr,
					   const struct sockaddr_in *fc_netaddr,
					   const struct sockaddr_in *fc_netmask)
{
	if (((fc_addr->sin_addr.s_addr ^ fc_netaddr->sin_addr.s_addr) &
		 fc_netmask->sin_addr.s_addr) == 0)
		return 1;
	else
		return 0;
}


#ifdef HAVE_IPV6

static int fc_range_sockaddr_AF_INET6(const struct sockaddr_in6 *fc_addr,
						const struct sockaddr_in6 *fc_netaddr,
						const struct sockaddr_in6 *fc_netmask)
{
	int			fc_i;

	for (fc_i = 0; fc_i < 16; fc_i++)
	{
		if (((fc_addr->sin6_addr.s6_addr[fc_i] ^ fc_netaddr->sin6_addr.s6_addr[fc_i]) &
			 fc_netmask->sin6_addr.s6_addr[fc_i]) != 0)
			return 0;
	}

	return 1;
}
#endif							/* HAVE_IPV6 */

/*
 *	pg_sockaddr_cidr_mask - 制作适当族和所需显著位数的网络掩码
 *
 * numbits 可以为 null，在这种情况下，掩码完全设置。
 *
 * 结果掩码被放入 *mask 中，最好足够大。
 *
 * 返回值为 0 表示正常，-1 表示异常。
 */
int pg_sockaddr_cidr_mask(struct sockaddr_storage *fc_mask, char *fc_numbits, int fc_family)
{
	long		fc_bits;
	char	   *fc_endptr;

	if (fc_numbits == NULL)
	{
		fc_bits = (fc_family == AF_INET) ? 32 : 128;
	}
	else
	{
		fc_bits = strtol(fc_numbits, &fc_endptr, 10);
		if (*fc_numbits == '\0' || *fc_endptr != '\0')
			return -1;
	}

	switch (fc_family)
	{
		case AF_INET:
			{
				struct sockaddr_in fc_mask4;
				long		fc_maskl;

				if (fc_bits < 0 || fc_bits > 32)
					return -1;
				memset(&fc_mask4, 0, sizeof(fc_mask4));
				/* 避免 "x << 32"，这不是可移植的 */
				if (fc_bits > 0)
					fc_maskl = (0xffffffffUL << (32 - (int) fc_bits))
						& 0xffffffffUL;
				else
					fc_maskl = 0;
				fc_mask4.sin_addr.s_addr = pg_hton32(fc_maskl);
				memcpy(fc_mask, &fc_mask4, sizeof(fc_mask4));
				break;
			}

#ifdef HAVE_IPV6
		case AF_INET6:
			{
				struct sockaddr_in6 fc_mask6;
				int			fc_i;

				if (fc_bits < 0 || fc_bits > 128)
					return -1;
				memset(&fc_mask6, 0, sizeof(fc_mask6));
				for (fc_i = 0; fc_i < 16; fc_i++)
				{
					if (fc_bits <= 0)
						fc_mask6.sin6_addr.s6_addr[fc_i] = 0;
					else if (fc_bits >= 8)
						fc_mask6.sin6_addr.s6_addr[fc_i] = 0xff;
					else
					{
						fc_mask6.sin6_addr.s6_addr[fc_i] =
							(0xff << (8 - (int) fc_bits)) & 0xff;
					}
					fc_bits -= 8;
				}
				memcpy(fc_mask, &fc_mask6, sizeof(fc_mask6));
				break;
			}
#endif
		default:
			return -1;
	}

	fc_mask->ss_family = fc_family;
	return 0;
}


/*
 * 在确保掩码对 addr 是合理的情况下，运行 addr/mask 的回调函数。
 */
static void fc_run_ifaddr_callback(PgIfAddrCallback fc_callback, void *fc_cb_data,
					struct sockaddr *fc_addr, struct sockaddr *fc_mask)
{
	struct sockaddr_storage fc_fullmask;

	if (!fc_addr)
		return;

	/* 检查掩码是否有效 */
	if (fc_mask)
	{
		if (fc_mask->sa_family != fc_addr->sa_family)
		{
			fc_mask = NULL;
		}
		else if (fc_mask->sa_family == AF_INET)
		{
			if (((struct sockaddr_in *) fc_mask)->sin_addr.s_addr == INADDR_ANY)
				fc_mask = NULL;
		}
#ifdef HAVE_IPV6
		else if (fc_mask->sa_family == AF_INET6)
		{
			if (IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *) fc_mask)->sin6_addr))
				fc_mask = NULL;
		}
#endif
	}

	/* 如果掩码无效，生成我们自己的完全设置的掩码 */
	if (!fc_mask)
	{
		pg_sockaddr_cidr_mask(&fc_fullmask, NULL, fc_addr->sa_family);
		fc_mask = (struct sockaddr *) &fc_fullmask;
	}

	(*fc_callback) (fc_addr, fc_mask, fc_cb_data);
}

#ifdef WIN32

#include <winsock2.h>
#include <ws2tcpip.h>

/*
 * 枚举系统的网络接口地址并为每一个调用回调。
 * 成功时返回 0，出错时返回 -1。
 *
 * 此版本适用于 Win32。使用 Winsock 2 函数（即：ws2_32.dll）
 */
int pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data)
{
	INTERFACE_INFO *ptr,
			   *ii = NULL;
#ifdef FDD //cppcheck
	unsigned long length = 0,
				i;
#else
	unsigned long length,
				i;
#endif
	unsigned long n_ii = 0;
	SOCKET		sock;
	int			error;

	sock = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0);
	if (sock == INVALID_SOCKET)
		return -1;

	while (n_ii < 1024)
	{
		n_ii += 64;
		ptr = realloc(ii, sizeof(INTERFACE_INFO) * n_ii);
		if (!ptr)
		{
			free(ii);
			closesocket(sock);
			errno = ENOMEM;
			return -1;
		}

		ii = ptr;
		if (WSAIoctl(sock, SIO_GET_INTERFACE_LIST, 0, 0,
					 ii, n_ii * sizeof(INTERFACE_INFO),
					 &length, 0, 0) == SOCKET_ERROR)
		{
			error = WSAGetLastError();
			if (error == WSAEFAULT || error == WSAENOBUFS)
				continue;		/* 需要增大缓冲区 */
			closesocket(sock);
			free(ii);
			return -1;
		}

		break;
	}

	for (i = 0; i < length / sizeof(INTERFACE_INFO); ++i)
		fc_run_ifaddr_callback(callback, cb_data,
							(struct sockaddr *) &ii[i].iiAddress,
							(struct sockaddr *) &ii[i].iiNetmask);

	closesocket(sock);
	free(ii);
	return 0;
}
#elif HAVE_GETIFADDRS			/* && !WIN32 */

#ifdef HAVE_IFADDRS_H
#include <ifaddrs.h>
#endif

/*
 * 枚举系统的网络接口地址并为每一个调用回调。
 * 成功时返回 0，出错时返回 -1。
 *
 * 此版本使用 getifaddrs() 接口，可在 BSD、AIX 和现代 Linux 上使用。
 */
int pg_foreach_ifaddr(PgIfAddrCallback fc_callback, void *fc_cb_data)
{
	struct ifaddrs *fc_ifa,
			   *fc_l;

	if (getifaddrs(&fc_ifa) < 0)
		return -1;

	for (fc_l = fc_ifa; fc_l; fc_l = fc_l->ifa_next)
		fc_run_ifaddr_callback(fc_callback, fc_cb_data,
							fc_l->ifa_addr, fc_l->ifa_netmask);

	freeifaddrs(fc_ifa);
	return 0;
}
#else							/* !HAVE_GETIFADDRS && !WIN32 */

#include <sys/ioctl.h>

#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif

#ifdef HAVE_SYS_SOCKIO_H
#include <sys/sockio.h>
#endif

/*
 * SIOCGIFCONF 在 Solaris 和 HP/UX 上不返回 IPv6 地址。
 * 所以我们优先使用 SIOCGLIFCONF 如果它可用。
 *
 * 然而在 HP/UX 上，它 *仅* 返回 IPv6 地址，
 * 结构体的名称也略有不同。
 * 我们还需要再次调用 SIOCGIFCONF 来获取
 * IPv4 地址。我们目前不做这个，只是在 HP/UX 上回退到 SIOCGIFCONF。
 */

#if defined(SIOCGLIFCONF) && !defined(__hpux)

/*
 * 枚举系统的网络接口地址并为每一个调用回调。
 * 成功时返回 0，出错时返回 -1。
 *
 * 此版本使用 ioctl(SIOCGLIFCONF)。
 */
int pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data)
{
	struct lifconf lifc;
	struct lifreq *lifr,
				lmask;
	struct sockaddr *addr,
			   *mask;
	char	   *ptr,
			   *buffer = NULL;
	size_t		n_buffer = 1024;
	pgsocket	sock,
				fd;

#ifdef HAVE_IPV6
	pgsocket	sock6;
#endif
	int			i,
				total;

	sock = socket(AF_INET, SOCK_DGRAM, 0);
	if (sock == PGINVALID_SOCKET)
		return -1;

	while (n_buffer < 1024 * 100)
	{
		n_buffer += 1024;
		ptr = realloc(buffer, n_buffer);
		if (!ptr)
		{
			free(buffer);
			close(sock);
			errno = ENOMEM;
			return -1;
		}

		memset(&lifc, 0, sizeof(lifc));
		lifc.lifc_family = AF_UNSPEC;
		lifc.lifc_buf = buffer = ptr;
		lifc.lifc_len = n_buffer;

		if (ioctl(sock, SIOCGLIFCONF, &lifc) < 0)
		{
			if (errno == EINVAL)
				continue;
			free(buffer);
			close(sock);
			return -1;
		}

		/*
		 * 一些 Unix 尝试返回尽可能多的数据，而不指示是否分配了足够的空间。
		 * 除非有很多冗余，否则不要相信我们拥有所有数据。
		 */
		if (lifc.lifc_len < n_buffer - 1024)
			break;
	}

#ifdef HAVE_IPV6
	/* 我们需要一个 IPv6 套接字来进行 SIOCGLIFNETMASK ioctls */
	sock6 = socket(AF_INET6, SOCK_DGRAM, 0);
	if (sock6 == PGINVALID_SOCKET)
	{
		free(buffer);
		close(sock);
		return -1;
	}
#endif

	total = lifc.lifc_len / sizeof(struct lifreq);
	lifr = lifc.lifc_req;
	for (i = 0; i < total; ++i)
	{
		addr = (struct sockaddr *) &lifr[i].lifr_addr;
		memcpy(&lmask, &lifr[i], sizeof(struct lifreq));
#ifdef HAVE_IPV6
		fd = (addr->sa_family == AF_INET6) ? sock6 : sock;
#else
		fd = sock;
#endif
		if (ioctl(fd, SIOCGLIFNETMASK, &lmask) < 0)
			mask = NULL;
		else
			mask = (struct sockaddr *) &lmask.lifr_addr;
		fc_run_ifaddr_callback(callback, cb_data, addr, mask);
	}

	free(buffer);
	close(sock);
#ifdef HAVE_IPV6
	close(sock6);
#endif
	return 0;
}
#elif defined(SIOCGIFCONF)

/*
 * 剩余的 Unix 使用 SIOCGIFCONF。一些只在这里返回 IPv4 信息，
 * 所以这是最不推荐的方法。注意，没有标准的方法来迭代返回的 ifreq 结构体数组。
 * 在某些操作系统中，结构体被填充到足以适应任何地址，
 * 在其他操作系统中你必须计算 ifreq 结构体的大小。
 */

/* 一些操作系统有 _SIZEOF_ADDR_IFREQ，因此只需使用它 */
#ifndef _SIZEOF_ADDR_IFREQ

/* 基于 sockaddr.sa_len 计算 */
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
#define _SIZEOF_ADDR_IFREQ(ifr) \
		((ifr).ifr_addr.sa_len > sizeof(struct sockaddr) ? \
		 (sizeof(struct ifreq) - sizeof(struct sockaddr) + \
		  (ifr).ifr_addr.sa_len) : sizeof(struct ifreq))

/* 填充的 ifreq 结构体，简单 */
#else
#define _SIZEOF_ADDR_IFREQ(ifr) \
	sizeof (struct ifreq)
#endif
#endif							/* !_SIZEOF_ADDR_IFREQ */

/*
 * 枚举系统的网络接口地址并为每一个调用回调。
 * 成功时返回 0，出错时返回 -1。
 *
 * 此版本使用 ioctl(SIOCGIFCONF)。
 */
int pg_foreach_ifaddr(PgIfAddrCallback fc_callback, void *fc_cb_data)
{
	struct ifconf fc_ifc;
	struct ifreq *fc_ifr,
			   *fc_end,
				fc_addr,
				fc_mask;
	char	   *fc_ptr,
			   *fc_buffer = NULL;
	size_t		fc_n_buffer = 1024;
	pgsocket	fc_sock;

	fc_sock = socket(AF_INET, SOCK_DGRAM, 0);
	if (fc_sock == PGINVALID_SOCKET)
		return -1;

	while (fc_n_buffer < 1024 * 100)
	{
		fc_n_buffer += 1024;
		fc_ptr = realloc(fc_buffer, fc_n_buffer);
		if (!fc_ptr)
		{
			free(fc_buffer);
			close(fc_sock);
			errno = ENOMEM;
			return -1;
		}

		memset(&fc_ifc, 0, sizeof(fc_ifc));
		fc_ifc.ifc_buf = fc_buffer = fc_ptr;
		fc_ifc.ifc_len = fc_n_buffer;

		if (ioctl(fc_sock, SIOCGIFCONF, &fc_ifc) < 0)
		{
			if (errno == EINVAL)
				continue;
			free(fc_buffer);
			close(fc_sock);
			return -1;
		}

		/*
		 * 一些 Unix 尝试返回尽可能多的数据，而不指示是否分配了足够的空间。
		 * 除非有很多冗余，否则不要相信我们拥有所有数据。
		 */
		if (fc_ifc.ifc_len < fc_n_buffer - 1024)
			break;
	}

	fc_end = (struct ifreq *) (fc_buffer + fc_ifc.ifc_len);
	for (fc_ifr = fc_ifc.ifc_req; fc_ifr < fc_end;)
	{
		memcpy(&fc_addr, fc_ifr, sizeof(fc_addr));
		memcpy(&fc_mask, fc_ifr, sizeof(fc_mask));
		if (ioctl(fc_sock, SIOCGIFADDR, &fc_addr, sizeof(fc_addr)) == 0 &&
			ioctl(fc_sock, SIOCGIFNETMASK, &fc_mask, sizeof(fc_mask)) == 0)
			fc_run_ifaddr_callback(fc_callback, fc_cb_data,
								&fc_addr.ifr_addr, &fc_mask.ifr_addr);
		fc_ifr = (struct ifreq *) ((char *) fc_ifr + _SIZEOF_ADDR_IFREQ(*fc_ifr));
	}

	free(fc_buffer);
	close(fc_sock);
	return 0;
}
#else							/* !defined(SIOCGIFCONF) */

/*
 * 枚举系统的网络接口地址，并为每个地址调用回调函数。
 * 如果成功返回 0，出现问题则返回 -1。
 *
 * 如果没有已知的方法获取接口地址，这个版本是我们的后备方案。
 * 只返回标准的回环地址。
 */
int pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data)
{
	struct sockaddr_in addr;
	struct sockaddr_storage mask;

#ifdef HAVE_IPV6
	struct sockaddr_in6 addr6;
#endif

	/* addr 127.0.0.1/8 */
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = pg_ntoh32(0x7f000001);
	memset(&mask, 0, sizeof(mask));
	pg_sockaddr_cidr_mask(&mask, "8", AF_INET);
	fc_run_ifaddr_callback(callback, cb_data,
						(struct sockaddr *) &addr,
						(struct sockaddr *) &mask);

#ifdef HAVE_IPV6
	/* addr ::1/128 */
	memset(&addr6, 0, sizeof(addr6));
	addr6.sin6_family = AF_INET6;
	addr6.sin6_addr.s6_addr[15] = 1;
	memset(&mask, 0, sizeof(mask));
	pg_sockaddr_cidr_mask(&mask, "128", AF_INET6);
	fc_run_ifaddr_callback(callback, cb_data,
						(struct sockaddr *) &addr6,
						(struct sockaddr *) &mask);
#endif

	return 0;
}
#endif							/* !defined(SIOCGIFCONF) */

#endif							/* !HAVE_GETIFADDRS */
